#ifndef AI_Perceiver_h__
#define AI_Perceiver_h__

//includes
#include <vector>
#include <map>
#include <d3d9.h>
#include <d3dx9math.h>

class BPhysicsActor;
class DirectXMesh;

namespace Perception
{
//The main enumeration
enum Ai_Perception_Key
{
    OCCLUDER,
    TARGET_0,
    TARGET_1,
    TARGET_2
};

//Ai specific vertex, positions and colors only
struct Ai_Perception_Vertex
{
    Ai_Perception_Vertex(): 
    pos(0,0,0,1),
    color(0,0,0,1)
    {}	
    Ai_Perception_Vertex(float posX, float posY, float posZ,
                         float colR, float colG, float colB) : 
    pos(posX, posY, posZ, 1),
    color(colR,colG,colB,1)
    {}

    D3DXVECTOR4 pos;
    D3DXVECTOR4 color;

    static IDirect3DVertexDeclaration9* s_dcl;
    static bool RegisterDeclarations();
    static void Release(){if(s_dcl)s_dcl->Release(); s_dcl = 0;}
};

// The mesh class that uses the Ai specific vertex type
class Ai_Perception_Mesh
{
public:
    Ai_Perception_Mesh() : 
        m_vBuffer(NULL),
        m_scaleX(1),
        m_scaleY(1),
        m_scaleZ(1){}
    ~Ai_Perception_Mesh(){if (m_vBuffer){m_vBuffer->Release();}}
    void CreateAsCube(  float scaleX, float scaleY, float scaleZ,
                        Ai_Perception_Key key);
    void CreateAsSphere(float radius, Ai_Perception_Key key);
    void CreateFromMesh(DirectXMesh* mesh, Ai_Perception_Key key,
                        float scaleX, float scaleY, float scaleZ);
    void Render(ID3DXEffect* effect);
    void CleanUp(); 
private:
    //either a sphere or a cube 
    LPDIRECT3DVERTEXBUFFER9 m_vBuffer;
    //dependant on type of mesh
    int m_numVerts;
public:
    //cube will use all 3, spheres will just use scaleX!
    float m_scaleX,m_scaleY,m_scaleZ;
};

//The objects that will get rendered
class Ai_Perception_Obj
{
public:
    Ai_Perception_Obj(Ai_Perception_Key key,D3DXMATRIXA16* trans,BPhysicsActor* owner) :   
        m_transformMatrix(trans),
        m_colorLookUp(key),
        m_owner(owner){}
    ~Ai_Perception_Obj(){m_mesh.CleanUp();}
    void CreateAsCube(float scaleX, float scaleY, float scaleZ);
    //A mesh must have its scale managed else where
    void CreateFromMesh(DirectXMesh* mesh,float scaleX, float scaleY, float scaleZ);
    void Render(ID3DXEffect* effect);
    BPhysicsActor* GetOwner(){return m_owner;}
private:
    //the mesh that represents this object
    Ai_Perception_Mesh      m_mesh;
    //the transofrmation matrix used by this mesh
    D3DXMATRIXA16*          m_transformMatrix;  
    //the key to the color this object will use
    Ai_Perception_Key       m_colorLookUp;
    BPhysicsActor* m_owner;
};

//The data required per AI render
class Ai_Perceiver;
struct Ai_Perception_Cache
{
    Ai_Perception_Cache(Ai_Perceiver* o) : m_finished(false),  
                            pViewMat(NULL),
                            pProjMat(NULL), 
                            pRenderTarget(NULL),
    owner(o){}
    bool m_finished;
    D3DXMATRIXA16* pViewMat;
    D3DXMATRIXA16* pProjMat;
    LPDIRECT3DTEXTURE9 pRenderTarget;
    Ai_Perceiver* owner;

};

//The management singleton
class Ai_Perception_Manager
{
#pragma region Singleton Implementation
private:
    Ai_Perception_Manager() : 
       m_framesSinceLastRender(0),
        m_minFrameDelay(0),
        m_maxFrameDelay(0),
        m_cacheThreshold(0)
       {}
    ~Ai_Perception_Manager() {if(Ai_Perception_Vertex::s_dcl)Ai_Perception_Vertex::s_dcl->Release();}
    Ai_Perception_Manager(const Ai_Perception_Manager& other){}
    Ai_Perception_Manager& operator=(const Ai_Perception_Manager* other){}
public:
    static Ai_Perception_Manager* Instance()
    {
        static Ai_Perception_Manager* g = NULL;
        if( g == NULL)
        {
            g = new Ai_Perception_Manager;
        }
        return g;
    }
    static void Kill() 
    {
        delete Instance();
    }
#pragma endregion

public:
    void Initialize(int maxFramewait, int minFrameWait, int requestThreshold);
    void RegisterObject(Ai_Perception_Obj* object);
    void UnregisterObject(Ai_Perception_Obj* object);
    void UnregisterAllObjects();
    void NewRequest(Ai_Perception_Cache* request)
    {
        m_requestCache.push_back(request);
        request->m_finished = false;
    }
    void Update();
    D3DXVECTOR3 GetColor(Ai_Perception_Key key);
    void DebugRenderObjects();
private:
    void RenderRequests();
    void GenerateBuffer(Ai_Perception_Cache* cache);
    void RenderObjects(ID3DXEffect* effect);

private:
    std::map<Ai_Perception_Key, D3DXVECTOR3> m_colorKey;
    int m_framesSinceLastRender;
    int m_minFrameDelay;
    int m_maxFrameDelay;
    unsigned int m_cacheThreshold;
    std::vector<Ai_Perception_Obj*> m_objects;
    std::vector<Ai_Perception_Cache*> m_requestCache;
public:
    //called through game
    void LAddColorCode(int key, float r, float g, float b);
};

//The main perceiver class
struct PixelColor8
{
    unsigned __int8 b;
    unsigned __int8 g;
    unsigned __int8 r;
    unsigned __int8 a;
};
class Ai_Perceiver
{
public:
    Ai_Perceiver(BPhysicsActor* o) : 
    m_hasActiveRequest(false),
    m_perceptionTex(NULL),
    m_owner(o),
    m_resX(1),
    m_resY(1),
    m_colorBuf(NULL)
    {
        m_request = new Ai_Perception_Cache(this);
        m_request->pViewMat = &m_ViewMatrix;
        m_request->pProjMat = &m_ProjMatrix;
    }
    ~Ai_Perceiver(){CleanUp();}

    void Initialize(int x, int y);
    void CleanUp();
    void NewRequest();
    void SetViewMatrix(const D3DXVECTOR3& eye, const D3DXVECTOR3& look,const D3DXVECTOR3&  up);
    void SetProjectionMatrixPerspective(float field, float aspect, float near, float far);
    void SetProjectionMatrixOrthographic(float width, float height, float near, float far);
    bool TextureReady();
    bool HasRequest()
    {
        return m_hasActiveRequest;
    }
    LPDIRECT3DTEXTURE9 GetPerceptionTexture()
    {
        return m_perceptionTex;
    }
    void RenderDebugInfo();
    D3DXVECTOR3 GetDirection(int x, int y);
    PixelColor8* GetBuffer() {return m_colorBuf;}
    int GetResX(){return m_resX;}
    int GetResY(){return m_resY;}
private:

    bool m_hasActiveRequest;
    Ai_Perception_Cache* m_request;
    //For rendering the AIs view.
    D3DXMATRIXA16 m_ViewMatrix;
    D3DXMATRIXA16 m_ProjMatrix;
    LPDIRECT3DTEXTURE9 m_perceptionTex;
    PixelColor8* m_colorBuf;
    int m_resX, m_resY;
    BPhysicsActor* m_owner;
    //debug only
    float m_fOv, m_aspect;  //perspective
    float m_w, m_h;         //ortho
    float  m_n, m_f;

    D3DXVECTOR3 m_eye;
    D3DXVECTOR3 m_look;
    D3DXVECTOR3 m_up;
};

};//Perception

#endif // AI_Perceiver_h__
